#include <stdio.h>
#include <string.h>
#include <stdlib.h> //malloc()
#include "auth.h"   //get_token
#include <curl/curl.h>
#include "cJSON.h"

//读取音频文件，返回音频文件大小
size_t stt_load_file(const char *file, char **pbuf)
{
    FILE *fp;
    //以只读方式打开文件
    fp = fopen(file, "r");
    if (fp == NULL) //打开文件失败，打印错误信息并退出
    {
        perror("fopen() failed");
        return 0;
    }

    //将文件指针定位到文件末尾
    fseek(fp, 0, SEEK_END);
    //获取文件指针的当前位置，即文件的大小
    long size = ftell(fp);

    *pbuf = malloc(size);
    //将文件指针重新定位到文件头
    fseek(fp, 0, SEEK_SET);

    //将文件中的内容读取到内存中
    fread(*pbuf, 1, size, fp);

    fclose(fp);

    return size;
}

char *stt_send_request(const char *token, const char *audio, size_t size)
{
    FILE *fp;

    //响应消息的地址
    char *response = NULL;
    //响应消息的长度
    size_t resplen = 0;
    //创建内存文件，当通过文件句柄写入数据时，会自动分配内存
    fp = open_memstream(&response, &resplen);
    if (fp == NULL) //打开文件失败，打印错误信息并退出
    {
        perror("open_memstream() failed");
        return NULL;
    }

    //初始化HTTP客户端
    CURL *curl = curl_easy_init();
    if (curl == NULL)
    {
        perror("curl_easy_init() failed");
        return NULL;
    }

    char *url = NULL;
    asprintf(&url, "http://vop.baidu.com/server_api?cuid=jxf&token=%s", token);
    //准备HTTP请求消息，设置API地址（URI）
    curl_easy_setopt(curl, CURLOPT_URL, url);
    //创建头部信息链表
    struct curl_slist *headers = NULL;
    //向链表中增加头部字段
    headers = curl_slist_append(headers, "Content-Type: audio/pcm;rate=16000");
    //设置HTTP请求头部字段
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    //配置客户端，使用HTTP的POST方法发送请求消息
    curl_easy_setopt(curl, CURLOPT_POST, 1);

    //配置需要通过POST请求消息发送给服务器的数据
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, audio);
    //指定发送数据的长度
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, size);

    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);

    //发送HTTP请求消息，等待服务器的响应消息
    CURLcode error = curl_easy_perform(curl);
    if (error != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(error));
        curl_easy_cleanup(curl);
        fclose(fp);
        free(response);
        free(url);
        return NULL;
    }

    //释放HTTP客户端申请的资源
    curl_easy_cleanup(curl);

    //关闭内存文件
    fclose(fp);

    //释放asprintf申请的内存
    free(url);

    return response;
}

char *stt_process_response(const char *response)
{
    cJSON *json = cJSON_Parse(response);
    if (json == NULL)
    {
        const char *error_pos = cJSON_GetErrorPtr();
        if (error_pos != NULL)
        {
            fprintf(stderr, "Error before: %s\n", error_pos);
        }
        return NULL;
    }
    cJSON *err_no = cJSON_GetObjectItemCaseSensitive(json, "err_no");
    if (err_no->valueint != 0)
    {
        cJSON *err_msg = cJSON_GetObjectItemCaseSensitive(json, "err_msg");
        fprintf(stderr, "%s\n", err_msg->valuestring);
        return NULL;
    }

    cJSON *result = cJSON_GetObjectItemCaseSensitive(json, "result");
    //获取数组中的第一个元素
    cJSON *text = cJSON_GetArrayItem(result, 0);
    return text->valuestring;
}

char *robot_make_request(const char *apikey, const char *text)
{
    //判断输入的字符串长度不为0
    if (strlen(text) == 0)
    {
        return NULL;
    }

    cJSON *request = cJSON_CreateObject();

    cJSON *perception = cJSON_CreateObject();
    cJSON *inputText = cJSON_CreateObject();

    cJSON_AddStringToObject(inputText, "text", text);
    cJSON_AddItemToObject(perception, "inputText", inputText);
    cJSON_AddItemToObject(request, "perception", perception);

    cJSON *userInfo = cJSON_CreateObject();
    cJSON_AddStringToObject(userInfo, "apiKey", apikey);
    cJSON_AddStringToObject(userInfo, "userId", "jxf");
    cJSON_AddItemToObject(request, "userInfo", userInfo);

    //将JSON数据结构转为字符串
    return cJSON_Print(request);
}

//作业：发送请求报文给图灵机器人服务器，等待服务器的响应报文
//     收到响应报文后，不需要解析，直接通过函数返回返回JSON字符串
char *robot_send_request(const char *request)
{
    FILE *fp;

    //响应消息的地址
    char *response = NULL;
    //响应消息的长度
    size_t resplen = 0;
    //创建内存文件，当通过文件句柄写入数据时，会自动分配内存
    fp = open_memstream(&response, &resplen);
    if (fp == NULL) //打开文件失败，打印错误信息并退出
    {
        perror("open_memstream() failed");
        return NULL;
    }

    //初始化HTTP客户端
    CURL *curl = curl_easy_init();
    if (curl == NULL)
    {
        perror("curl_easy_init() failed");
        return NULL;
    }

    //准备HTTP请求消息，设置API地址（URI）
    curl_easy_setopt(curl, CURLOPT_URL, "http://openapi.tuling123.com/openapi/api/v2");

    //配置客户端，使用HTTP的POST方法发送请求消息
    curl_easy_setopt(curl, CURLOPT_POST, 1);

    //配置需要通过POST请求消息发送给服务器的数据
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);

    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    //发送HTTP请求消息，等待服务器的响应消息
    CURLcode error = curl_easy_perform(curl);
    if (error != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(error));
        curl_easy_cleanup(curl);
        fclose(fp);
        free(response);
        return NULL;
    }

    //释放HTTP客户端申请的资源
    curl_easy_cleanup(curl);

    //关闭内存文件
    fclose(fp);

    return response;
}

//处理机器人响应报文
char *robot_process_response(const char *response)
{
    cJSON *json = cJSON_Parse(response);
    if (json == NULL)
    {
        const char *error_pos = cJSON_GetErrorPtr();
        if (error_pos != NULL)
        {
            fprintf(stderr, "Error before: %s\n", error_pos);
        }
        return NULL;
    }
    cJSON *intent = cJSON_GetObjectItemCaseSensitive(json, "intent");
    if (intent == NULL)
    {
        fprintf(stderr, "Get intent object failed\n");
        cJSON_Delete(json);
        return NULL;
    }
    cJSON *code = cJSON_GetObjectItemCaseSensitive(intent, "code");

    if (code->valueint < 10000)
    {
        fprintf(stderr, "Robot error:%d\n", code->valueint);
        cJSON_Delete(json);
        return NULL;
    }
    cJSON *results = cJSON_GetObjectItemCaseSensitive(json, "results");
    cJSON *result;
    cJSON_ArrayForEach(result, results) //遍历数组
    {
        cJSON *resultType = cJSON_GetObjectItemCaseSensitive(result, "resultType");
        if (strcmp(resultType->valuestring, "text") == 0)
        {
            cJSON *values = cJSON_GetObjectItemCaseSensitive(result, "values");
            cJSON *text = cJSON_GetObjectItemCaseSensitive(values, "text");
            return text->valuestring;
        }
    }

    cJSON_Delete(json);
}

//将文本转换为语音
void text2speech(const char *token, const char *text)
{
    CURL *curl = curl_easy_init();

    //发送到百度云的字符串需要进行2次URL编码
    char *temp = curl_easy_escape(curl, text, strlen(text));
    char *data = curl_easy_escape(curl, temp, strlen(temp));
    curl_free(temp);

    //拼接POST请求发送的数据
    char *postdata;
    asprintf(&postdata, "tex=%s&lan=zh&cuid=jxf&ctp=1&aue=6&tok=%s", data, token);
    curl_free(data);

    //启动播放软件，通过管道写入音频数据
    FILE *fp = popen("aplay -q -", "w");
    if (fp == NULL)
    {
        perror("fopen() failed");
        return;
    }

    curl_easy_setopt(curl, CURLOPT_URL, "https://tsn.baidu.com/text2audio");
    //配置客户端，使用HTTP的POST方法发送请求消息
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    //配置需要通过POST请求消息发送给服务器的数据
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    //发送HTTP请求消息，等待服务器的响应消息
    CURLcode error = curl_easy_perform(curl);
    if (error != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(error));
        curl_easy_cleanup(curl);
        free(postdata);
        pclose(fp);
        return;
    }

    //释放HTTP客户端申请的资源
    curl_easy_cleanup(curl);
    free(postdata);

    //关闭管道
    pclose(fp);
}

int main()
{
    //指向音频数据的指针
    char *audio = NULL;
    //音频数据大小
    size_t size;
    //读取PCM文件内容
    size = stt_load_file("test.pcm", &audio);
    if (size == 0)
    {
        return EXIT_FAILURE;
    }

    char *token = get_token("sfFawBe8OiQfoF5gReHVIqu4", "hpyhhxQDx5zYCaNriEO5Z0Qj2EqF0uYl");
    if (NULL == token)
    {
        free(audio);
        return EXIT_FAILURE;
    }
    //将语音数据发送给云服务器，等待响应报文
    char *stt_response = stt_send_request(token, audio, size);
    free(audio);
    if (stt_response == NULL)
    {
        return EXIT_FAILURE;
    }
    //处理响应报文，将文本数据交给robot.c
    const char *text = stt_process_response(stt_response);

    char *apikey = "26ab863fdffa47e29b834a36501e87bf";

    //构造请求报文
    char *request = robot_make_request(apikey, text);
    if (request == NULL)
    {
        return 0;
    }
    //将请求报文发送给图灵机器人
    char *robot_response = robot_send_request(request);
    free(request);
    if (robot_response == NULL)
    {
        return 0;
    }
    const char *robot_text = robot_process_response(robot_response);
    free(stt_response);
    free(robot_response);
    //将读入的文本转换为语音
    text2speech(token, robot_text);
    free(token);
    return 0;
}